package xyz.xtt.equity.service.impl;

import static xyz.xtt.common.asset.AssertUtils.isFalse;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.extern.slf4j.Slf4j;
import xyz.xtt.asset.client.request.AssetNoInfoRequest;
import xyz.xtt.asset.client.response.TblAssetNoDto;
import xyz.xtt.equity.api.dto.PackageDto;
import xyz.xtt.equity.api.dto.TblBenefitPackageDto;
import xyz.xtt.equity.api.dto.TblUserBenefitByAssetIdDto;
import xyz.xtt.equity.api.dto.TblUserBenefitByPackageDto;
import xyz.xtt.equity.api.enums.BenefitEnum;
import xyz.xtt.equity.api.enums.BenefitPurchasePayStatusEnum;
import xyz.xtt.equity.api.enums.TblBenefitPackageEnum;
import xyz.xtt.equity.api.enums.UserBenefitSourceTypeEnum;
import xyz.xtt.equity.client.request.TblUserBenefitDto;
import xyz.xtt.equity.constants.ErrorMessage;
import xyz.xtt.equity.entity.BenefitMetaData;
import xyz.xtt.equity.entity.TblAssetBenefitPackage;
import xyz.xtt.equity.entity.TblBenefit;
import xyz.xtt.equity.entity.TblBenefitPackage;
import xyz.xtt.equity.entity.TblBenefitPurchaseRecord;
import xyz.xtt.equity.entity.TblUserBenefit;
import xyz.xtt.equity.entity.TblUserBenefitChangeRecord;
import xyz.xtt.equity.exception.ChainException;
import xyz.xtt.equity.service.IBenefitService;
import xyz.xtt.equity.service.IInnerService;
import xyz.xtt.equity.service.ITblAssetBenefitPackageService;
import xyz.xtt.equity.service.ITblBenefitPackageService;
import xyz.xtt.equity.service.ITblBenefitPurchaseRecordService;
import xyz.xtt.equity.service.ITblBenefitService;
import xyz.xtt.equity.service.ITblUserBenefitChangeRecordService;
import xyz.xtt.equity.service.ITblUserBenefitService;
import xyz.xtt.equity.utils.RedisLockUtil;

/**
 * @author dale
 * @date 2025/2/11
 **/
@Slf4j
@Service
public class BenefitServiceImpl implements IBenefitService {
	private static final Integer USER_BENEFIT_BLOCKED = 1;

	private static final LocalDateTime MAX_EXPIRE_TIME = LocalDateTime.of(9999, 12, 31, 23, 59, 59);
	private static final String USER_BENEFIT_CONSUME_CACHE = "userBenefitConsumeCache:%d:%s:%d";
	private static final String USER_DAILY_MINT_RECEIVE_CACHE = "nft:userDailyMintBenefitReceiveCache:%d";
	@Autowired
	private ITblUserBenefitService tblUserBenefitService;
	@Autowired
	private ITblUserBenefitChangeRecordService tblUserBenefitChangeRecordService;
	@Autowired
	private RedisLockUtil redisLockUtil;
	@Autowired
	private ITblBenefitPurchaseRecordService tblBenefitPurchaseRecordService;
	@Autowired
	private ITblBenefitPackageService iTblBenefitPackageService;
	@Autowired
	private ITblBenefitService tblBenefitService;
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private IInnerService iInnerService;
	@Autowired
	private ITblBenefitPurchaseRecordService iTblBenefitPurchaseRecordService;
	@Autowired
	private ITblAssetBenefitPackageService iTblAssetBenefitPackageService;

	/**
	 * 查询用户权益数据
	 * 
	 * @param appUserId
	 * @param benefitCodes 多个benefitCode逗号分隔
	 * @return
	 */
	@Override
	public Map<String, Integer> getUserBenefits(long appUserId, String benefitCodes) {
		Map<String, Integer> result = new HashMap<>();
		if (StringUtils.isBlank(benefitCodes)) {
			return result;
		}
		// 初始化map
		for (String benefitCode : benefitCodes.split(",")) {
			result.put(benefitCode, 0);
		}

		List<TblUserBenefit> userBenefits = tblUserBenefitService.queryUserValidBenefitsByCode(appUserId, List.of(benefitCodes.split(",")));
		userBenefits.forEach(tblUserBenefit -> {
			result.put(tblUserBenefit.getBenefitCode(), result.get(tblUserBenefit.getBenefitCode()) + tblUserBenefit.getBenefitRemain());
		});

		if (result.containsKey(BenefitEnum.FREE_MINT.getCode())) {
			// 用户没有免费铸造次数时检查用户是否领取过用户初始铸造权益
			/*
			 * TblUserBenefit initBenefit =
			 * tblUserBenefitService.queryUserInitMintBenefit(appUserId); if (initBenefit ==
			 * null){
			 * sendFreeBenefitPackage(TblBenefitPackageEnum.USER_INIT_MINT_BENEFIT.name(),
			 * appUserId,1, UserBenefitSourceTypeEnum.USER_INIT_MINT_BENEFIT,null); return
			 * getUserBenefits(appUserId,benefitCodes); }
			 */

			// 检查用户是否领取过当日免费铸造权益,如果没有领取过则进行自动领取操作
			return autoReceiveFreeMintBenefit(appUserId, benefitCodes, result);
		}
		return result;
	}

	/**
	 * 自动领取当日免费铸造权益
	 * 
	 * @param appUserId
	 * @param benefitCodes
	 * @param result
	 * @return
	 */
	private Map<String, Integer> autoReceiveFreeMintBenefit(long appUserId, String benefitCodes, Map<String, Integer> result) {
		String redisKey = String.format(USER_DAILY_MINT_RECEIVE_CACHE, appUserId);
		LocalDateTime now = LocalDateTime.now();
		Duration DurationUntilEndOfDay = Duration.between(now, LocalDateTime.of(now.toLocalDate(), LocalTime.MAX));
		boolean setCacheResult = redisTemplate.opsForValue().setIfAbsent(redisKey, String.valueOf(System.currentTimeMillis()), DurationUntilEndOfDay);
		if (setCacheResult) {
			recordAndSendFreeBenefitPackage(TblBenefitPackageEnum.USER_PER_DAY_MINT_BENEFIT
					.name(), appUserId, 1, UserBenefitSourceTypeEnum.USER_PER_DAY_MINT_BENEFIT_RECEIVE, null);
			return getUserBenefits(appUserId, benefitCodes);
		}
		return result;
	}

	/**
	 * 用户权益消耗
	 *
	 * @param appUserId         用户id
	 * @param benefitCode       权益code
	 * @param consumeNum        消耗数量
	 * @param operateBusinessId 操作业务id。可空。比如铸造消耗权益时传铸造审核记录id
	 * @param operateDesc       操作描述。比如铸造消耗权益时传：铸造审核或更具体信息
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean userBenefitConsumed(@Positive long appUserId, @NotNull String benefitCode, @Positive int consumeNum, Long operateBusinessId,
			@NotNull String operateDesc) {
		// 用户权益操作分布式锁保护
		String lockKey = "userBenefitOperate:" + appUserId;
		String lockValue = IdUtil.fastSimpleUUID();
		boolean locked = false;
		try {
			locked = redisLockUtil.tryLock(lockKey, lockValue, 1, TimeUnit.MINUTES, 6, TimeUnit.SECONDS);
			if (locked) {
				String redisKey = String.format(USER_BENEFIT_CONSUME_CACHE, appUserId, benefitCode, operateBusinessId);
				// 添加缓存防止接口被重复调用
				if (operateBusinessId != null && operateBusinessId > 0L) {
					String consumed = redisTemplate.opsForValue().get(redisKey);
					if (StringUtils.isNotBlank(consumed)) {
						throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_REPEAT);
					}
				}

				long batchId = IdUtil.getSnowflakeNextId();
				LocalDateTime now = LocalDateTime.now();
				List<TblUserBenefitChangeRecord> changeRecords = new ArrayList<>();
				// 按照时间先后顺序对用户多次获得的权益依次进行消耗直至消耗数量达到consumeNum
				List<TblUserBenefit> userBenefits = tblUserBenefitService.queryUserValidBenefitsByCodeForUpdate(appUserId, List.of(benefitCode));
				for (TblUserBenefit userBenefit : userBenefits) {
					if (Objects.equals(userBenefit.getBenefitStatus(), USER_BENEFIT_BLOCKED)) {
						continue;
					}
					if (consumeNum <= 0) {
						break;
					}
					int remain = userBenefit.getBenefitRemain();
					if (remain <= 0) {
						continue;
					}
					int consume = Math.min(remain, consumeNum);
					boolean updated = tblUserBenefitService.updateBenefitRemain(userBenefit.getUserBenefitId(), remain - consume, remain);
					if (!updated) {
						throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_FAILED);
					}
					TblUserBenefitChangeRecord changeRecord = TblUserBenefitChangeRecord
							.builder()
							.recordId(IdUtil.getSnowflakeNextId())
							.batchId(batchId)
							.appUserId(appUserId)
							.userBenefitId(userBenefit.getUserBenefitId())
							.changeType(2)
							.changeAmount(consume)
							.beforeChange(remain)
							.afterChange(remain - consume)
							.operateDesc(operateDesc)
							.operateBusinessId(operateBusinessId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build();
					changeRecords.add(changeRecord);
					consumeNum -= consume;
				}
				if (consumeNum > 0) {
					throw new ChainException(ErrorMessage.USER_BENEFIT_NOT_ENOUGH);
				} else if (!tblUserBenefitChangeRecordService.saveBatch(changeRecords)) {
					throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_FAILED);
				}

				// 添加缓存防止接口被重复调用
				if (operateBusinessId != null && operateBusinessId > 0L) {
					redisTemplate.opsForValue().set(redisKey, String.valueOf(System.currentTimeMillis()), 1, TimeUnit.MINUTES);
				}
				return true;
			} else {
				log.error("用户权益消耗失败，获取分布式锁失败");
				throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_FAILED);
			}
		} finally {
			if (locked) {
				redisLockUtil.releaseLock(lockKey, lockValue);
			}
		}
	}

	/**
	 * 用户权益消耗
	 *
	 * @param appUserId         用户id
	 * @param benefitCode       权益code
	 * @param consumeNum        消耗数量
	 * @param operateBusinessId 操作业务id。可空。比如铸造消耗权益时传铸造审核记录id
	 * @param operateDesc       操作描述。比如铸造消耗权益时传：铸造审核或更具体信息
	 * @return 返回实际消耗次数
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public int userBenefitConsumeAsMuchAsPossible(long appUserId, String benefitCode, int consumeNum, Long operateBusinessId, String operateDesc) {
		// 用户权益操作分布式锁保护
		String lockKey = "userBenefitOperate:" + appUserId;
		String lockValue = IdUtil.fastSimpleUUID();
		boolean locked = false;
		try {
			locked = redisLockUtil.tryLock(lockKey, lockValue, 1, TimeUnit.MINUTES, 6, TimeUnit.SECONDS);
			if (locked) {
				String redisKey = String.format(USER_BENEFIT_CONSUME_CACHE, appUserId, benefitCode, operateBusinessId);
				// 添加缓存防止接口被重复调用
				if (operateBusinessId != null && operateBusinessId > 0L) {
					String consumed = redisTemplate.opsForValue().get(redisKey);
					if (StringUtils.isNotBlank(consumed)) {
						throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_REPEAT);
					}
				}

				int actualConsumed = 0;
				long batchId = IdUtil.getSnowflakeNextId();
				LocalDateTime now = LocalDateTime.now();
				List<TblUserBenefitChangeRecord> changeRecords = new ArrayList<>();
				// 按照时间先后顺序对用户多次获得的权益依次进行消耗直至消耗数量达到consumeNum
				List<TblUserBenefit> userBenefits = tblUserBenefitService.queryUserValidBenefitsByCodeForUpdate(appUserId, List.of(benefitCode));
				for (TblUserBenefit userBenefit : userBenefits) {
					if (Objects.equals(userBenefit.getBenefitStatus(), USER_BENEFIT_BLOCKED)) {
						continue;
					}
					if (consumeNum <= 0) {
						break;
					}
					int remain = userBenefit.getBenefitRemain();
					if (remain <= 0) {
						continue;
					}
					int consume = Math.min(remain, consumeNum);
					boolean updated = tblUserBenefitService.updateBenefitRemain(userBenefit.getUserBenefitId(), remain - consume, remain);
					if (!updated) {
						throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_FAILED);
					}
					TblUserBenefitChangeRecord changeRecord = TblUserBenefitChangeRecord
							.builder()
							.recordId(IdUtil.getSnowflakeNextId())
							.batchId(batchId)
							.appUserId(appUserId)
							.userBenefitId(userBenefit.getUserBenefitId())
							.changeType(2)
							.changeAmount(consume)
							.beforeChange(remain)
							.afterChange(remain - consume)
							.operateDesc(operateDesc)
							.operateBusinessId(operateBusinessId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build();
					changeRecords.add(changeRecord);
					consumeNum -= consume;
					actualConsumed += consume;
				}
				if (CollectionUtil.isEmpty(changeRecords)) {
					return actualConsumed;
				}
				if (!tblUserBenefitChangeRecordService.saveBatch(changeRecords)) {
					throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_FAILED);
				}
				// 添加缓存防止接口被重复调用
				if (operateBusinessId != null && operateBusinessId > 0L) {
					redisTemplate.opsForValue().set(redisKey, String.valueOf(System.currentTimeMillis()), 1, TimeUnit.MINUTES);
				}
				return actualConsumed;
			} else {
				log.error("用户权益消耗失败，获取分布式锁失败");
				throw new ChainException(ErrorMessage.USER_BENEFIT_CONSUME_FAILED);
			}
		} finally {
			if (locked) {
				redisLockUtil.releaseLock(lockKey, lockValue);
			}
		}
	}

	/**
	 * 恢复已消耗用户权益
	 *
	 * @param appUserId         用户id
	 * @param operateBusinessId 操作业务id。比如铸造审核不通过时恢复权益时传铸造审核记录id
	 * @param operateDesc       操作描述。比如铸造审核不通过恢复权益时传：铸造审核不通过或更具体信息
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void userBenefitRecover(@Positive long appUserId, @Positive Long operateBusinessId, @NotNull String operateDesc) {
		// 用户权益操作分布式锁保护
		String lockKey = "userBenefitOperate:" + appUserId;
		String lockValue = IdUtil.fastSimpleUUID();
		boolean locked = false;
		try {
			locked = redisLockUtil.tryLock(lockKey, lockValue, 1, TimeUnit.MINUTES, 6, TimeUnit.SECONDS);
			if (locked) {
				// 检查是否已经恢复过权益
				List<TblUserBenefitChangeRecord> recoverRecords = tblUserBenefitChangeRecordService
						.queryUserBenefitChangeRecords(appUserId, 3, operateBusinessId);
				if (recoverRecords.size() > 0) {
					log.info("用户权益已恢复，无需重复操作");
					throw new ChainException(ErrorMessage.USER_BENEFIT_RECOVER_FAILED);
				}

				// 查询用户已消耗的权益
				List<TblUserBenefitChangeRecord> changeRecords = tblUserBenefitChangeRecordService
						.queryUserBenefitChangeRecords(appUserId, 2, operateBusinessId);
				if (changeRecords.size() == 0) {
					throw new ChainException(ErrorMessage.USER_BENEFIT_RECOVER_FAILED);
				}
				long batchId = IdUtil.getSnowflakeNextId();
				LocalDateTime now = LocalDateTime.now();
				for (TblUserBenefitChangeRecord changeRecord : changeRecords) {
					TblUserBenefit userBenefit = tblUserBenefitService.getById(changeRecord.getUserBenefitId());
					int remain = userBenefit.getBenefitRemain();
					boolean updated = tblUserBenefitService
							.updateBenefitRemain(userBenefit.getUserBenefitId(), remain + changeRecord.getChangeAmount(), remain);
					if (!updated) {
						throw new ChainException(ErrorMessage.USER_BENEFIT_RECOVER_FAILED);
					}
					TblUserBenefitChangeRecord recoverRecord = TblUserBenefitChangeRecord
							.builder()
							.recordId(IdUtil.getSnowflakeNextId())
							.batchId(batchId)
							.appUserId(appUserId)
							.userBenefitId(userBenefit.getUserBenefitId())
							.changeType(3)
							.changeAmount(changeRecord.getChangeAmount())
							.beforeChange(remain)
							.afterChange(remain + changeRecord.getChangeAmount())
							.operateDesc(operateDesc)
							.operateBusinessId(operateBusinessId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build();
					recoverRecords.add(recoverRecord);
				}
				if (!tblUserBenefitChangeRecordService.saveBatch(recoverRecords)) {
					throw new ChainException(ErrorMessage.USER_BENEFIT_RECOVER_FAILED);
				}
			} else {
				log.error("用户权益恢复失败，获取分布式锁失败");
				throw new ChainException(ErrorMessage.USER_BENEFIT_RECOVER_FAILED);
			}
		} finally {
			if (locked) {
				redisLockUtil.releaseLock(lockKey, lockValue);
			}
		}
	}

	/**
	 * 获取3日内VIP将要过期的用户id集合
	 *
	 * @param pageNo      页码
	 * @param pageSize    每页数量
	 * @param benefitCode 权益code
	 * @return
	 */
	@Override
	public List<TblUserBenefitDto> getExpiringUserBenefits(int pageNo, int pageSize, String benefitCode) {
		LocalDateTime startTime = LocalDateTime.now().plusMinutes(10);
		LocalDateTime endTime = LocalDateTime.now().plusDays(3);
		LambdaQueryChainWrapper<TblUserBenefit> query = tblUserBenefitService
				.lambdaQuery()
				.eq(TblUserBenefit::getBenefitCode, benefitCode)
				.eq(TblUserBenefit::getBenefitStatus, 0)
				.ge(TblUserBenefit::getExpireAt, startTime)
				.lt(TblUserBenefit::getExpireAt, endTime)
				.orderByAsc(TblUserBenefit::getExpireAt);
		Page<TblUserBenefit> page = query.page(new Page<>(pageNo, pageSize));
		if (page.getTotal() > 0) {
			return page.getRecords().stream().map(this::convertUserBenefit2Dto).toList();
		}
		return Collections.emptyList();
	}

	/**
	 * 权益发放接口（用于系统规则下自动向用户发放权益）
	 *
	 * @param packageCode    权益包代码
	 * @param appUserId      下发目标用户id
	 * @param sendNum        发放数量
	 * @param sourceTypeEnum 权益来源
	 * @param sourceRefId    权益来源关联业务id
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void sendBenefitPackage(String packageCode, long appUserId, int sendNum, UserBenefitSourceTypeEnum sourceTypeEnum, Long sourceRefId,
			Long benefitPurchaseRecordId) {
		TblBenefitPackage benefitPackage = iTblBenefitPackageService.getPackageByCode(packageCode);
		if (benefitPackage == null) {
			throw new ChainException(ErrorMessage.BENEFIT_PACKAGE_NOT_EXIST);
		}
		this.sendBenefitPackage(benefitPackage, appUserId, sendNum, sourceTypeEnum, sourceRefId, benefitPurchaseRecordId);
	}

	/**
	 * 权益发放同时记录权益领取记录
	 *
	 * @param packageCode
	 * @param appUserId
	 * @param sendNum
	 * @param sourceTypeEnum
	 * @param sourceRefId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void recordAndSendFreeBenefitPackage(String packageCode, long appUserId, int sendNum, UserBenefitSourceTypeEnum sourceTypeEnum, Long sourceRefId) {
		TblBenefitPackage benefitPackage = iTblBenefitPackageService.getPackageByCode(packageCode);
		if (benefitPackage == null) {
			throw new ChainException(ErrorMessage.BENEFIT_PACKAGE_NOT_EXIST);
		}
		LocalDateTime now = LocalDateTime.now();
		TblBenefitPurchaseRecord purchaseRecord = TblBenefitPurchaseRecord
				.builder()
				.recordId(IdUtil.getSnowflakeNextId())
				.appUserId(appUserId)
				.packageId(benefitPackage.getPackageId())
				.purchaseNum(sendNum)
				.unitPrice(BigDecimal.ZERO)
				.totalPaid(BigDecimal.ZERO)
				.payStatus(BenefitPurchasePayStatusEnum.PAID.code())
				.createTime(now)
				.updateTime(now)
				.isDel(0)
				.biz_type(sourceTypeEnum.name())
				.build();
		tblBenefitPurchaseRecordService.save(purchaseRecord);

		this.sendBenefitPackage(benefitPackage, appUserId, sendNum, sourceTypeEnum, sourceRefId, purchaseRecord.getRecordId());
	}

	@Override
	public void sendBenefitPackage(TblBenefitPackage benefitPackage, long appUserId, int sendNum, UserBenefitSourceTypeEnum sourceTypeEnum, Long sourceRefId,
			Long benefitPurchaseRecord) {
		List<TblBenefit> benefits = tblBenefitService.queryBenefitsByPackageId(benefitPackage.getPackageId());
		List<TblUserBenefit> userBenefits = new ArrayList<>();
		List<TblUserBenefitChangeRecord> changeRecords = new ArrayList<>();
		LocalDateTime now = LocalDateTime.now();
		long batchId = IdUtil.getSnowflakeNextId();
		benefits.stream().forEach(benefit -> {
			BenefitMetaData metaData = JSONUtil.toBean(benefit.getMetaJson(), BenefitMetaData.class);
			LocalDateTime startAt, expireAt;
			if (Objects.equals(metaData.getStartAtType(), 2)) {
				// 从当天零时开始
				startAt = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
			} else if (Objects.equals(metaData.getStartAtType(), 1)) {
				// 从当前时刻开始
				startAt = LocalDateTime.now();
			} else {
				// 从同benefit_code最近一条记录的失效时间开始
				BenefitEnum benefitEnum = BenefitEnum.getByCode(benefit.getBenefitCode());
				if (benefitEnum == null) {
					throw new ChainException(ErrorMessage.BENEFIT_NOT_EXIST);
				}
				TblUserBenefit latestUserBenefit = tblUserBenefitService.queryLatestUserBenefit(appUserId, benefitEnum);
				// 已经存在生效期内的权益时，从上一条记录的过期时间开始计算新的有效期
				if (latestUserBenefit != null && latestUserBenefit.getExpireAt().isAfter(LocalDateTime.now())) {
					startAt = latestUserBenefit.getExpireAt();
				} else {
					startAt = LocalDateTime.now();
				}
			}
			if (Objects.equals(metaData.getLimitDays(), -1)) {
				// 如果权益有效期为-1，则权益有效期为永久。过期时间为9999-12-31 23:59:59
				expireAt = MAX_EXPIRE_TIME;
			} else {
				// 如果上一条权益是永久有效，则当前权益的过期时间仍为永久有效。避免超过上限
				if (Objects.equals(startAt, MAX_EXPIRE_TIME)) {
					expireAt = MAX_EXPIRE_TIME;
				} else {
					expireAt = startAt.plusDays(metaData.getLimitDays() * sendNum);
				}
			}

			long userBenefitId = IdUtil.getSnowflakeNextId();
			userBenefits
					.add(TblUserBenefit
							.builder()
							.userBenefitId(userBenefitId)
							.appUserId(appUserId)
							.benefitId(benefit.getBenefitId())
							.benefitCode(benefit.getBenefitCode())
							.benefitNum(metaData.getLimitCount() * sendNum)
							.benefitRemain(metaData.getLimitCount() * sendNum)
							.startTime(startAt)
							.expireAt(expireAt)
							.sourceType(sourceTypeEnum.name())
							.sourceRefId(sourceRefId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build());
			changeRecords
					.add(TblUserBenefitChangeRecord
							.builder()
							.recordId(IdUtil.getSnowflakeNextId())
							.batchId(batchId)
							.appUserId(appUserId)
							.userBenefitId(userBenefitId)
							.changeType(1)
							.changeAmount(metaData.getLimitCount() * sendNum)
							.beforeChange(0)
							.afterChange(metaData.getLimitCount() * sendNum)
							.operateDesc(sourceTypeEnum.getDesc())
							.operateBusinessId(sourceRefId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build());
		});
		tblUserBenefitService.saveBatch(userBenefits);
		tblUserBenefitChangeRecordService.saveBatch(changeRecords);
	}

	private TblUserBenefitDto convertUserBenefit2Dto(TblUserBenefit benefit) {
		TblUserBenefitDto dto = new TblUserBenefitDto();
		BeanUtils.copyProperties(benefit, dto);
		return dto;
	}

	/**
	 * 发送资产关联的权益
	 *
	 * @param assetId
	 * @param assetNodIds
	 * @param senderId
	 * @param receiverId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void sendAssetNoBenefit(Long assetId, List<Long> assetNodIds, Long senderId, Long receiverId) {
		log.info("start sendAssetNoBenefit. assetId:{} assetNodIds:{} senderId:{} receiverId:{}", assetId, assetNodIds, senderId, receiverId);
		// 检查资产是否存在权益包
		List<TblAssetBenefitPackage> assetBenefitPackages = iTblAssetBenefitPackageService.getByAssetId(assetId);
		if (CollectionUtils.isEmpty(assetBenefitPackages)) {
			log.warn("assetId:{} has no benefit package", assetId);
			return;
		}

		// 检查权益包是否已经发放给买家
		List<TblBenefitPurchaseRecord> buyerPurchaseRecords = tblBenefitPurchaseRecordService.getAssetNoPurchaseRecordsNotBlocked(assetNodIds, receiverId);
		if (!CollectionUtils.isEmpty(buyerPurchaseRecords)) {
			if (buyerPurchaseRecords.stream().anyMatch(record -> Objects.equals(record.getBenefitStatus(), 0))) {
				log.warn("assetNodIds:{} has already been sent to receiverId:{}", assetNodIds, receiverId);
				return;
			}
		}

		List<TblBenefitPurchaseRecord> sellerPurchaseRecords = tblBenefitPurchaseRecordService.getAssetNoPurchaseRecords(assetNodIds, senderId);
		if (CollectionUtils.isEmpty(sellerPurchaseRecords)) {
			// 卖家并不存在相关权益说明是首次领取，此时从资产管理的权益包中领取完整权益
			sendAssetNoBenefitForFirstLevelTrade(assetNodIds, assetBenefitPackages, receiverId);
			return;
		}

		if (sellerPurchaseRecords.get(0).getBenefitStatus().equals(0)) {
			// 卖家权益未被冻结，此时追加冻结
			log.warn("转移权益时发现卖家权益未被冻结，assetNodIds:{} senderId:{} receiverId:{}", assetNodIds, senderId, receiverId);
			blockAssetNoBenefit(assetNodIds, senderId);
		}

		// 商品循环交易可能导致卖家在相同编号上拥有多个权益领取记录，取最新的一条记录
		Map<String, TblBenefitPurchaseRecord> map = new HashMap<>(sellerPurchaseRecords.size());
		sellerPurchaseRecords.forEach(record -> {
			String key = record.getAssetNoId() + "_" + record.getPackageId();
			if (!map.containsKey(key) || map.get(key).getCreateTime().isBefore(record.getCreateTime())) {
				map.put(key, record);
			}
		});
		sellerPurchaseRecords = new ArrayList<>(map.values());

		// 卖家存在相关权益，说明是二级交易，此时根据卖家剩余的权益情况为买家发放权益
		LocalDateTime now = LocalDateTime.now();
		List<TblBenefitPurchaseRecord> newBuyerPurchaseRecords = new ArrayList<>();
		sellerPurchaseRecords.stream().forEach(record -> {
			Long recordId = IdUtil.getSnowflakeNextId();
			newBuyerPurchaseRecords
					.add(TblBenefitPurchaseRecord
							.builder()
							.recordId(recordId)
							.appUserId(receiverId)
							.assetNoId(record.getAssetNoId())
							.packageId(record.getPackageId())
							.purchaseNum(1)
							.payStatus(1)
							.biz_type(record.getBiz_type())
							.benefitStatus(0)
							.createTime(now)
							.updateTime(now)
							.build());
			transBenefitPackage(record, receiverId, UserBenefitSourceTypeEnum.BUY_NFT_WITH_BENEFIT, recordId);
		});
		iTblBenefitPurchaseRecordService.saveBatch(newBuyerPurchaseRecords);
	}

	private void sendAssetNoBenefitForFirstLevelTrade(List<Long> assetNodIds, List<TblAssetBenefitPackage> assetBenefitPackages, Long receiverId) {
		List<TblBenefitPurchaseRecord> purchaseRecords = new ArrayList<>();
		LocalDateTime now = LocalDateTime.now();
		boolean needInsertPackageMap = true;
		for (Long assetNodId : assetNodIds) {
			for (TblAssetBenefitPackage assetBenefitPackage : assetBenefitPackages) {
				purchaseRecords
						.add(TblBenefitPurchaseRecord
								.builder()
								.recordId(IdUtil.getSnowflakeNextId())
								.appUserId(receiverId)
								.assetNoId(assetNodId)
								.packageId(assetBenefitPackage.getPackageId())
								.purchaseNum(1)
								.payStatus(1)
								.biz_type(UserBenefitSourceTypeEnum.BUY_NFT_WITH_BENEFIT.name())
								.createTime(now)
								.updateTime(now)
								.build());
			}
		}
		if (CollectionUtils.isEmpty(purchaseRecords)) {
			return;
		}
		isFalse(iTblBenefitPurchaseRecordService.saveBatch(purchaseRecords), ErrorMessage.USER_BENEFIT_UPDATE_FAILED);
		purchaseRecords.forEach((record) -> {
			sendBenefitPackage(record.getPackageId(), receiverId, record.getPurchaseNum(), UserBenefitSourceTypeEnum.BUY_NFT_WITH_BENEFIT, null, record
					.getRecordId());
		});
	}

	private void sendBenefitPackage(Long packageId, Long receiverId, Integer purchaseNum, UserBenefitSourceTypeEnum sourceTypeEnum, Long sourceRefId,
			Long benefitPurchaseRecordId) {
		List<TblBenefit> benefits = tblBenefitService.queryBenefitsByPackageId(packageId);
		List<TblUserBenefit> userBenefits = new ArrayList<>();
		List<TblUserBenefitChangeRecord> changeRecords = new ArrayList<>();
		LocalDateTime now = LocalDateTime.now();
		long batchId = IdUtil.getSnowflakeNextId();
		benefits.stream().forEach(benefit -> {
			BenefitMetaData metaData = JSONUtil.toBean(benefit.getMetaJson(), BenefitMetaData.class);
			LocalDateTime startAt, expireAt;
			if (Objects.equals(metaData.getStartAtType(), 2)) {
				// 从当天零时开始
				startAt = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
			} else if (Objects.equals(metaData.getStartAtType(), 1)) {
				// 从当前时刻开始
				startAt = LocalDateTime.now();
			} else {
				// 从同benefit_code最近一条记录的失效时间开始
				BenefitEnum benefitEnum = BenefitEnum.getByCode(benefit.getBenefitCode());
				if (benefitEnum == null) {
					throw new ChainException(ErrorMessage.BENEFIT_NOT_EXIST);
				}
				TblUserBenefit latestUserBenefit = tblUserBenefitService.queryLatestUserBenefit(receiverId, benefitEnum);
				// 已经存在生效期内的权益时，从上一条记录的过期时间开始计算新的有效期
				if (latestUserBenefit != null && latestUserBenefit.getExpireAt().isAfter(LocalDateTime.now())) {
					startAt = latestUserBenefit.getExpireAt();
				} else {
					startAt = LocalDateTime.now();
				}
			}
			if (Objects.equals(metaData.getLimitDays(), -1)) {
				// 如果权益有效期为-1，则权益有效期为永久。过期时间为9999-12-31 23:59:59
				expireAt = MAX_EXPIRE_TIME;
			} else {
				// 如果上一条权益是永久有效，则当前权益的过期时间仍为永久有效。避免超过上限
				if (Objects.equals(startAt, MAX_EXPIRE_TIME)) {
					expireAt = MAX_EXPIRE_TIME;
				} else {
					expireAt = startAt.plusDays(metaData.getLimitDays() * purchaseNum);
				}
			}

			long userBenefitId = IdUtil.getSnowflakeNextId();
			userBenefits
					.add(TblUserBenefit
							.builder()
							.userBenefitId(userBenefitId)
							.appUserId(receiverId)
							.benefitId(benefit.getBenefitId())
							.benefitCode(benefit.getBenefitCode())
							.benefitNum(metaData.getLimitCount() * purchaseNum)
							.benefitRemain(metaData.getLimitCount() * purchaseNum)
							.startTime(startAt)
							.expireAt(expireAt)
							.sourceType(sourceTypeEnum.name())
							.sourceRefId(sourceRefId)
							.benefitPurchaseRecordId(benefitPurchaseRecordId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build());
			changeRecords
					.add(TblUserBenefitChangeRecord
							.builder()
							.recordId(IdUtil.getSnowflakeNextId())
							.batchId(batchId)
							.appUserId(receiverId)
							.userBenefitId(userBenefitId)
							.changeType(1)
							.changeAmount(metaData.getLimitCount() * purchaseNum)
							.beforeChange(0)
							.afterChange(metaData.getLimitCount() * purchaseNum)
							.operateDesc(sourceTypeEnum.getDesc())
							.operateBusinessId(sourceRefId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build());
		});
		tblUserBenefitService.saveBatch(userBenefits);
		tblUserBenefitChangeRecordService.saveBatch(changeRecords);
	}

	/**
	 * 用户权益包权益转移
	 * 
	 * @param sellerPurchaseRecord      卖家权益包购买记录
	 * @param receiverId                接收人id
	 * @param userBenefitSourceTypeEnum 来源
	 * @param newBuyerPurchaseRecordId  接收人权益领取记录id
	 */
	private void transBenefitPackage(TblBenefitPurchaseRecord sellerPurchaseRecord, Long receiverId, UserBenefitSourceTypeEnum userBenefitSourceTypeEnum,
			Long newBuyerPurchaseRecordId) {
		List<TblUserBenefit> sellerBenefits = tblUserBenefitService.queryNotExpiredUserBenefitsByPurchaseRecord(sellerPurchaseRecord.getRecordId());
		List<TblUserBenefit> newBuyerBenefits = new ArrayList<>();
		List<TblUserBenefitChangeRecord> changeRecords = new ArrayList<>();
		LocalDateTime now = LocalDateTime.now();
		long batchId = IdUtil.getSnowflakeNextId();

		sellerBenefits.stream().forEach(sellerBenefit -> {
			boolean isForeverValid = sellerBenefit.getExpireAt().truncatedTo(ChronoUnit.SECONDS).isEqual(MAX_EXPIRE_TIME);

			// 计算权益开始结束时间
			LocalDateTime startAt, expireAt;
			if (isForeverValid) {
				startAt = LocalDateTime.now();
				expireAt = MAX_EXPIRE_TIME;
			} else {
				// 从同benefit_code最近一条记录的失效时间开始
				BenefitEnum benefitEnum = BenefitEnum.getByCode(sellerBenefit.getBenefitCode());
				if (benefitEnum == null) {
					throw new ChainException(ErrorMessage.BENEFIT_NOT_EXIST);
				}
				TblUserBenefit latestUserBenefit = tblUserBenefitService.queryLatestUserBenefit(receiverId, benefitEnum);
				// 已经存在生效期内的权益时，从上一条记录的过期时间开始计算新的有效期
				if (latestUserBenefit != null && latestUserBenefit.getExpireAt().isAfter(LocalDateTime.now())) {
					startAt = latestUserBenefit.getExpireAt();
				} else {
					startAt = LocalDateTime.now();
				}

				if (sellerBenefit.getBlockTime().isAfter(sellerBenefit.getExpireAt())) {
					expireAt = startAt;
				} else {
					Duration duration;
					if (sellerBenefit.getBlockTime().isBefore(sellerBenefit.getStartTime())) {
						duration = Duration.between(sellerBenefit.getStartTime(), sellerBenefit.getExpireAt());
					} else {
						duration = Duration.between(sellerBenefit.getBlockTime(), sellerBenefit.getExpireAt());
					}
					expireAt = startAt.plus(duration);
				}
			}

			long userBenefitId = IdUtil.getSnowflakeNextId();
			newBuyerBenefits
					.add(TblUserBenefit
							.builder()
							.userBenefitId(userBenefitId)
							.appUserId(receiverId)
							.benefitId(sellerBenefit.getBenefitId())
							.benefitCode(sellerBenefit.getBenefitCode())
							.benefitNum(sellerBenefit.getBenefitRemain())
							.benefitRemain(sellerBenefit.getBenefitRemain())
							.startTime(startAt)
							.expireAt(expireAt)
							.sourceType(userBenefitSourceTypeEnum.name())
							.benefitPurchaseRecordId(newBuyerPurchaseRecordId)
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build());
			changeRecords
					.add(TblUserBenefitChangeRecord
							.builder()
							.recordId(IdUtil.getSnowflakeNextId())
							.batchId(batchId)
							.appUserId(receiverId)
							.userBenefitId(userBenefitId)
							.changeType(1)
							.changeAmount(sellerBenefit.getBenefitRemain())
							.beforeChange(0)
							.afterChange(sellerBenefit.getBenefitRemain())
							.operateDesc(userBenefitSourceTypeEnum.getDesc())
							.createTime(now)
							.updateTime(now)
							.isDel(0)
							.build());
		});
		tblUserBenefitService.saveBatch(newBuyerBenefits);
		tblUserBenefitChangeRecordService.saveBatch(changeRecords);
	}

	@Override
	public List<TblBenefitPackageDto> getPackageByIds(List<Long> packageIds) {
		List<TblBenefitPackage> packages = iTblBenefitPackageService.lambdaQuery().in(TblBenefitPackage::getPackageId, packageIds).list();
		return convertBenefitPackage2Dto(packages);
	}

	private List<TblBenefitPackageDto> convertBenefitPackage2Dto(List<TblBenefitPackage> packages) {
		if (CollectionUtils.isEmpty(packages)) {
			return List.of();
		}
		return packages.stream().map(item -> {
			TblBenefitPackageDto dto = new TblBenefitPackageDto();
			BeanUtils.copyProperties(item, dto);
			return dto;
		}).toList();
	}

	@Override
	public Boolean blockAssetNoBenefit(List<Long> assetNodIds, Long userId) {
		List<TblBenefitPurchaseRecord> purchaseRecords = tblBenefitPurchaseRecordService.getAssetNoPurchaseRecords(assetNodIds, userId);
		if (CollectionUtils.isEmpty(purchaseRecords)) {
			return Boolean.FALSE;
		}
		isFalse(tblBenefitPurchaseRecordService.blockBenefitStatus(assetNodIds, userId), ErrorMessage.USER_BENEFIT_UPDATE_FAILED);
		Boolean blockUserBenefitByPurchaseRecord = tblUserBenefitService
				.blockUserBenefitByPurchaseRecord(purchaseRecords.stream().map(TblBenefitPurchaseRecord::getRecordId).toList());
		return blockUserBenefitByPurchaseRecord;
	}

	@Override
	public Boolean unBlockAssetNoBenefit(List<Long> assetNodIds, Long userId) {
		List<TblBenefitPurchaseRecord> purchaseRecords = tblBenefitPurchaseRecordService.getAssetNoPurchaseRecords(assetNodIds, userId);
		if (CollectionUtils.isEmpty(purchaseRecords)) {
			return Boolean.FALSE;
		}
		isFalse(tblBenefitPurchaseRecordService.unblockBenefitStatus(assetNodIds, userId), ErrorMessage.USER_BENEFIT_UPDATE_FAILED);
		Boolean unBlockUserBenefitByPurchaseRecord = tblUserBenefitService
				.unBlockUserBenefitByPurchaseRecord(purchaseRecords.stream().map(TblBenefitPurchaseRecord::getRecordId).toList());
		return unBlockUserBenefitByPurchaseRecord;
	}

	@Override
	public TblUserBenefitByAssetIdDto getBenefitDetailInfo(String uid, String assetId) {
		org.springframework.util.Assert.isTrue(StringUtils.isNotBlank(uid) || StringUtils.isNotBlank(assetId), "uid and assetId must not be null");
		TblUserBenefitByAssetIdDto result = new TblUserBenefitByAssetIdDto();
		AssetNoInfoRequest assetNoInfoRequest = new AssetNoInfoRequest();
		assetNoInfoRequest.setAssetId(String.valueOf(assetId));
		assetNoInfoRequest.setOwnerId(String.valueOf(uid));
		List<TblAssetNoDto> data = iInnerService.getNFTNoInfo(assetNoInfoRequest);
		if (CollectionUtil.isNotEmpty(data)) {
			List<Long> list = data.stream().map(TblAssetNoDto::getId).toList();
			// 通过编号查找用户权益下单记录 一对多
			List<TblBenefitPurchaseRecord> benefitPurchaseRecords = tblBenefitPurchaseRecordService
					.lambdaQuery()
					.in(TblBenefitPurchaseRecord::getAssetNoId, list)
					.eq(TblBenefitPurchaseRecord::getAppUserId, Long.valueOf(uid))
					.orderByDesc(TblBenefitPurchaseRecord::getCreateTime)
					.list();
			// 过滤数据
			Map<String, TblBenefitPurchaseRecord> purchaseMap = new HashMap<>();
			for (TblBenefitPurchaseRecord benefitPurchaseRecord : benefitPurchaseRecords) {
				String key = benefitPurchaseRecord.getAssetNoId() + "_" + benefitPurchaseRecord.getAppUserId() + "_" + benefitPurchaseRecord.getPackageId();
				if (purchaseMap.containsKey(key)) {
					continue;
				}
				purchaseMap.put(key, benefitPurchaseRecord);
			}
			benefitPurchaseRecords = new ArrayList<>(purchaseMap.values());

			Map<Long, List<TblBenefitPurchaseRecord>> purchaseRecordMap = benefitPurchaseRecords
					.stream()
					.collect(Collectors.groupingBy(TblBenefitPurchaseRecord::getAssetNoId));
			List<Long> tblBenefitPurchaseRecordIds = benefitPurchaseRecords.stream().map(TblBenefitPurchaseRecord::getRecordId).toList();
			Map<Long, List<TblUserBenefit>> collect;
			if (!CollectionUtils.isEmpty(tblBenefitPurchaseRecordIds)) {
				List<TblUserBenefit> list1 = tblUserBenefitService
						.lambdaQuery()
						.in(TblUserBenefit::getBenefitPurchaseRecordId, tblBenefitPurchaseRecordIds)
						.list();
				collect = list1.stream().collect(Collectors.groupingBy(TblUserBenefit::getBenefitPurchaseRecordId));
			} else {
				collect = Map.of();
			}
			data.forEach(assetNoDto -> {
				LinkedHashMap<String, List<TblUserBenefitByPackageDto>> userBenefitMap = result.getUserBenefitMap();
				userBenefitMap.computeIfAbsent(assetNoDto.getAssetNo(), k -> new ArrayList<>());
				if (purchaseRecordMap.get(assetNoDto.getId()) == null) {
					return;
				}
				for (TblBenefitPurchaseRecord benefitPurchaseRecord : purchaseRecordMap.get(assetNoDto.getId())) {
					List<TblUserBenefit> userBenefits = collect.get(benefitPurchaseRecord.getRecordId());
					if (userBenefits == null || userBenefits.isEmpty()) {
						continue;
					}
					Long packageId = benefitPurchaseRecord.getPackageId();
					TblBenefitPackage packageById = iTblBenefitPackageService.getPackageById(packageId);
					TblUserBenefitByPackageDto tblUserBenefitByPackageDto = new TblUserBenefitByPackageDto();
					PackageDto packageDto = new PackageDto();
					packageDto.setPackageId(packageId);
					packageDto.setPackageName(packageById.getPackageName());
					tblUserBenefitByPackageDto.getUserBenefitMap().put(packageDto, userBenefits.stream().map(this::convertUserBenefit2DtoByType).toList());
					userBenefitMap.get(assetNoDto.getAssetNo()).add(tblUserBenefitByPackageDto);
				}
			});
		} else {
			List<TblAssetBenefitPackage> byAssetId = iTblAssetBenefitPackageService.getByAssetId(Long.valueOf(assetId));
			if (CollectionUtils.isEmpty(byAssetId)) {
				return result;
			}
			Map<Long, TblBenefitPackage> packageMap = iTblBenefitPackageService
					.lambdaQuery()
					.in(TblBenefitPackage::getPackageId, byAssetId.stream().map(TblAssetBenefitPackage::getPackageId).toList())
					.list()
					.stream()
					.collect(Collectors.toMap(TblBenefitPackage::getPackageId, Function.identity()));
			byAssetId.forEach(assetBenefitPackage -> {
				List<TblBenefit> benefits = tblBenefitService.queryBenefitsByPackageId(assetBenefitPackage.getPackageId());
				result.getUserBenefitMap().computeIfAbsent("", k -> new ArrayList<>());
				List<TblUserBenefitByPackageDto> tblUserBenefitByPackageDtos = result.getUserBenefitMap().get("");
				TblUserBenefitByPackageDto tblUserBenefitByPackageDto = new TblUserBenefitByPackageDto();
				tblUserBenefitByPackageDtos.add(tblUserBenefitByPackageDto);
				LinkedHashMap<PackageDto, List<TblUserBenefitDto>> userBenefitMap = tblUserBenefitByPackageDto.getUserBenefitMap();
				List<TblUserBenefitDto> objects = new ArrayList<>();
				userBenefitMap
						.put(new PackageDto(
								assetBenefitPackage.getPackageId(),
								packageMap.containsKey(assetBenefitPackage.getPackageId()) ? packageMap.get(assetBenefitPackage.getPackageId()).getPackageName()
										: ""), objects);
				for (TblBenefit benefit : benefits) {
					BenefitMetaData metaData = JSONUtil.toBean(benefit.getMetaJson(), BenefitMetaData.class);
					result.getUserBenefitMap().computeIfAbsent("", k -> new ArrayList<>());
					BenefitEnum benefitEnum = BenefitEnum.getByCode(benefit.getBenefitCode());
					if (benefitEnum != null) {
						TblUserBenefitDto dto = new TblUserBenefitDto();
						dto.setIsDel(0);
						dto.setBenefitCode(benefit.getBenefitCode());
						dto.setBenefitId(benefit.getBenefitId());
						// 0:次数 1:时间 2 :次数+时间
						dto.setBenefitNum((benefitEnum.getValueType() & 1) == 1 ? metaData.getLimitDays() : metaData.getLimitCount());
						dto.setBenefitRemain((benefitEnum.getValueType() & 1) == 1 ? metaData.getLimitDays() : metaData.getLimitCount());
						objects.add(dto);
					}
				}

			});
		}
		return result;
	}

	private TblUserBenefitDto convertUserBenefit2DtoByType(TblUserBenefit benefit) {
		TblUserBenefitDto dto = new TblUserBenefitDto();
		BeanUtils.copyProperties(benefit, dto);
		if (dto.getBenefitRemain() == 0) {
			dto.setIsDel(1);
			return dto;
		}
		LocalDateTime now = LocalDateTime.now();
		// 说明要计算时间的权益
		if (dto.getBenefitRemain() < 0) {
			// 根据开始结束时间推算 剩余多少天 如果只有0 天 向上+1 否则正常取即可
			if (now.isAfter(dto.getExpireAt())) {
				dto.setIsDel(1);
				return dto;
			} else {
				dto.setBenefitRemain(Math.max(1, (int) ChronoUnit.DAYS.between(dto.getStartTime(), dto.getExpireAt())));
			}
		}
		return dto;
	}
}
